#include <cassert>
#include <cstdio>
#include <systemc>
#include <tlm>
#include <tlm_utils/simple_initiator_socket.h>
#include <tlm_utils/simple_target_socket.h>
#include <tlm_utils/peq_with_cb_and_phase.h>

using namespace sc_core;
using namespace sc_dt;
using namespace tlm_utils;
using namespace std;

// 辅助函数：将 tlm_phase 转换为字符串
const char* phase_to_string(const tlm::tlm_phase& phase) {
    switch (phase) {
        case tlm::BEGIN_REQ:   return "BEGIN_REQ";
        case tlm::END_REQ:     return "END_REQ";
        case tlm::BEGIN_RESP:  return "BEGIN_RESP";
        case tlm::END_RESP:    return "END_RESP";
        default:               return "UNKNOWN_PHASE";
    }
}

SC_MODULE(A) {
    SC_CTOR(A) {
        SC_THREAD(process);
        // 注册 nb_transport_bw 回调函数, 用于接收 Target 的 Response
        socket.register_nb_transport_bw(this, &A::nb_transport_bw);
    }
    tlm_utils::simple_initiator_socket<A> socket;

    // 用于同步
    sc_event response_event;

    void process() {
        // 设置 Request Payload
        tlm::tlm_generic_payload *payload = new tlm::tlm_generic_payload();
        char req_data[128];

        for (int i = 0; i < 8; ++i) {
            sprintf(req_data, "RequestData%d", i);
            payload->set_data_ptr((unsigned char *) req_data);
            payload->set_data_length(strlen(req_data) + 1);  // 设置数据长度
            payload->set_command(tlm::TLM_WRITE_COMMAND);  // 设置为写命令

            sc_time delay = SC_ZERO_TIME;
            tlm::tlm_phase phase = tlm::BEGIN_REQ; // 事务阶段: 初始为 BEGIN_REQ

            // Forward Path
            printf("@%s\t[%s] FW <%s>, data=%s\n", sc_time_stamp().to_string().c_str(), this->name(), phase_to_string(phase), req_data);
            tlm::tlm_sync_enum status = socket->nb_transport_fw(*payload, phase, delay);

            // 检查同步状态(nb_transport_fw 的返回值)
            if (status == tlm::TLM_UPDATED) {
                printf("@%s\t[%s] FW <%s>\n", sc_time_stamp().to_string().c_str(), this->name(), phase_to_string(phase));
                wait(response_event);
            } else {
                SC_REPORT_ERROR("TLM", "Unexpected forward path status");
            }
            printf("=================================================\n");
            wait(10, SC_NS); // 模拟两次事务间隔
        }

        printf("@%s\t[%s] TRANS DONE\n", sc_time_stamp().to_string().c_str(), this->name());

        // 释放 payload 对象
        delete payload;
    }

    // Backward Path 回调：接收 Target 的 Response
    tlm::tlm_sync_enum nb_transport_bw(tlm::tlm_generic_payload &payload, tlm::tlm_phase &phase, sc_time &delay) {
        const char *data = reinterpret_cast<const char *>(payload.get_data_ptr());
        printf("@%s\t[%s] BW <RECV RESP>, data=%s\n", sc_time_stamp().to_string().c_str(), this->name(), data);
        // 检查 TLM Phase
        if (phase == tlm::BEGIN_RESP) {
            phase = tlm::END_RESP; // 更新 Phase 为 END_RESP, 表示 Response 结束
            response_event.notify(delay); // 唤醒 process 线程
            return tlm::TLM_COMPLETED; // 返回 TLM_COMPLETED, 表示事务完成
        }
        return tlm::TLM_ACCEPTED;
    }
};

SC_MODULE(B) {
    SC_CTOR(B): peq("peq", this, &B::peq_cb) {
        // 注册 Forward Path 回调, 用于处理 Initiator 的请求
        socket.register_nb_transport_fw(this, &B::nb_transport_fw);
    }
    tlm_utils::simple_target_socket<B> socket;
    peq_with_cb_and_phase<B> peq; // Payload Event Queue
    int count = 0;

    // Forward Path 回调：处理 Initiator 的请求
    tlm::tlm_sync_enum nb_transport_fw(tlm::tlm_generic_payload &payload, tlm::tlm_phase &phase, sc_time &delay) {
        const char *req_data = reinterpret_cast<const char *>(payload.get_data_ptr());
        printf("@%s\t[%s] FW <RECV REQ>, data=%s\n", sc_time_stamp().to_string().c_str(), this->name(), req_data);

        // 检查 TLM Phase
        if (phase != tlm::BEGIN_REQ) {
            payload.set_response_status(tlm::TLM_GENERIC_ERROR_RESPONSE);
            return tlm::TLM_COMPLETED; // 返回 TLM_COMPLETED, 表示事务完成 (错误情况)
        }

        sc_time process_delay = sc_time(7, SC_NS); // 模拟处理延迟 7ns
        phase = tlm::END_REQ; // 更新阶段为 END_REQ, 表示 Request 结束
        peq.notify(payload, phase, process_delay); // 将事务放入 PEQ, 延迟处理
        printf("@%s\t[%s] FW [put payload into PEQ] [delay %s]\n", sc_time_stamp().to_string().c_str(), this->name(), process_delay.to_string().c_str());

        return tlm::TLM_UPDATED; // 返回 TLM_UPDATED, 表示 Phase 或 Delay 已更新
    }

    // PEQ 回调函数：处理事务
    void peq_cb(tlm::tlm_generic_payload& payload, const tlm::tlm_phase& phase) {
        assert(phase == tlm::END_REQ); // 仅处理 END_REQ 阶段

        // 设置 Response Payload
        char resp_data[128];
        sprintf(resp_data, "ResponseData%d", count++);
        payload.set_data_ptr((unsigned char *) resp_data);
        payload.set_data_length(strlen(resp_data) + 1); // 设置数据长度
        payload.set_response_status(tlm::TLM_OK_RESPONSE); // 设置响应状态为 OK

        tlm::tlm_phase resp_phase = tlm::BEGIN_RESP; // 设置 Phase: 初始为 BEGIN_RESP
        sc_time delay = sc_time(3, SC_NS); // 模拟处理时间 3ns
        printf("@%s\t[%s] BW <%s> data=%s, [in `peq_cb`] [delay %s]\n", sc_time_stamp().to_string().c_str(), this->name(), phase_to_string(resp_phase), resp_data, delay.to_string().c_str());
        tlm::tlm_sync_enum status = socket->nb_transport_bw(payload, resp_phase, delay);

        if (status == tlm::TLM_COMPLETED) {
            printf("@%s\t[%s] BW <%s> [in `peq_cb`]\n", sc_time_stamp().to_string().c_str(), this->name(), phase_to_string(resp_phase));
        } else {
            SC_REPORT_ERROR("TLM", "Unexpected backward path status");
        }
    }
};

int sc_main(int argc, char* argv[]) {
    // 实例化模块并绑定 Socket
    A a { "A" };
    B b { "B" };
    a.socket.bind(b.socket);

    // 启动仿真
    sc_start(1000, SC_NS);

    return EXIT_SUCCESS;
}
